use std::collections::HashMap;

use serde_derive::{Deserialize, Serialize};

trait AnimalTrait {
    fn say(&self);
}
#[derive(Deserialize, Serialize, Debug)]
struct Dog {
    name: String,
    work: String,
}
impl Dog {
    fn new(name: String, work: String) -> Dog {
        Dog { name, work }
    }
    fn say(&self) {
        println!("{} say wangwang", self.name);
    }
    fn get_work(&self) {
        println!("{}", self.work);
    }
}
impl AnimalTrait for Dog {
    fn say(&self) {
        println!("{} say wangwang", self.name);
    }
}

#[derive(Deserialize, Serialize, Debug)]
struct Cat {
    name: String,
    age: i32,
    work: String,
}

impl Cat {
    fn new(name: String) -> Cat {
        Cat {
            name: name,
            age: 0,
            work: "".to_string(),
        }
    }
    fn say(&self) {
        println!("{} say miamiamia", self.name);
    }
    fn get_age(&self) {
        println!("{}", self.age);
    }
}

impl AnimalTrait for Cat {
    fn say(&self) {
        println!("{} say miamiamia", self.name);
    }
}

#[derive(Deserialize, Serialize, Debug)]
#[serde(untagged)]
enum Animal {
    Cat(Cat),
    Dog(Dog),
}

impl Animal {
    fn say(&self) {
        match self {
            Animal::Cat(cat) => cat.say(),
            Animal::Dog(dog) => dog.say(),
        }
    }
}

#[derive(Deserialize, Serialize, Debug)]
#[serde(untagged)]
enum AnimalType {
    Cat,
    Dog,
}

impl AnimalType {
    fn str_to_animal_type(str: &str) -> AnimalType {
        match str {
            "dog" => AnimalType::Dog,
            "cat" => AnimalType::Cat,
            _ => panic!("unknown type"),
        }
    }
    fn str_to_animal(str: &str) -> Box<dyn AnimalTrait> {
        match str {
            "dog" => Box::new(Dog::new("admin".to_string(), "play".to_string())),
            "cat" => Box::new(Cat::new("test".to_string())),
            _ => panic!("unknown type"),
        }
    }
}

impl From<&str> for Box<dyn AnimalTrait> {
    fn from(value: &str) -> Self {
        match value {
            "dog" => Box::new(Dog::new("admin".to_string(), "play".to_string())),
            "cat" => Box::new(Cat::new("test".to_string())),
            _ => panic!("unknown type"),
        }
    }
}

type AnimalDynType = Box<dyn Fn() -> Box<dyn AnimalTrait>>;
struct AnimalFactory {
    map: HashMap<String, AnimalDynType>,
}
impl AnimalFactory {
    fn new() -> Self {
        let mut map = HashMap::new();

        // 显式指定闭包的返回类型为 Box<dyn AnimalTrait>
        // 显式将闭包类型包装为 Box<dyn Fn() -> Box<dyn AnimalTrait>>，避免类型不匹配
        // map.insert(
        //     "dog".to_string(),
        //     Box::new(|| {
        //         Box::new(Dog::new("admin".to_string(), "play".to_string())) as Box<dyn AnimalTrait>
        //     }) as Box<dyn Fn() -> Box<dyn AnimalTrait>>,
        // );
        // map.insert(
        //     "cat".to_string(),
        //     Box::new(|| Box::new(Cat::new("test".to_string())) as Box<dyn AnimalTrait>)
        //         as Box<dyn Fn() -> Box<dyn AnimalTrait>>,
        // );
        map.insert(
            "dog".to_string(),
            Box::new(|| {
                Box::new(Dog::new("admin".to_string(), "play".to_string())) as Box<dyn AnimalTrait>
            }) as AnimalDynType,
        );
        map.insert(
            "cat".to_string(),
            Box::new(|| Box::new(Cat::new("test".to_string()))),
        );

        AnimalFactory { map }
    }

    fn get_animal(&self, name: &str) -> Box<dyn AnimalTrait> {
        match self.map.get(name) {
            Some(create_fn) => create_fn(),
            None => panic!("not found"),
        }
    }
}

fn main() {
    let data = r#"
    {
        "name":"admin",
        "age":2,
        "work":"play"
    }
    "#;
    // let animal: Animal = serde_json::from_str(data).unwrap();
    let animal = serde_json::from_str::<Animal>(data).unwrap();
    // match animal {
    //     Animal::Dog(dog) => {
    //         dog.say();
    //     }
    //     Animal::Cat(cat) => {
    //         cat.say();
    //     }
    // };
    animal.say();

    let names = "dog";

    match AnimalType::str_to_animal_type(names) {
        AnimalType::Dog => {
            let dog = Dog {
                name: "admin".to_string(),
                work: "play".to_string(),
            };
            dog.say();
        }
        AnimalType::Cat => {
            let cat = Cat {
                name: "admin".to_string(),
                age: 2,
                work: "play".to_string(),
            };

            cat.say();
        }
    }

    let animal = AnimalType::str_to_animal(names);

    animal.say();

    let dog: Box<dyn AnimalTrait> = "dog".into();
    dog.say();

    let animal = AnimalFactory::new();

    let dog = animal.get_animal("dog");
    dog.say();
}
